/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package txservice

import (
	"sync"

	"hundsun.com/hsl/hschain/p2p/common/p2perror"

	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/p2p/impl/dht/protocol"
	"hundsun.com/hsl/hschain/protos"
	"hundsun.com/hsl/hschain/store/mq"
)

var once sync.Once

//Protocol Protocol
type Protocol struct {
	*protocol.P2PEnv
}

func init() {
	protocol.RegisterProtocolIniter(InitProtocol)
}

// InitProtocol init protocol
func InitProtocol(env *protocol.P2PEnv) {
	protocolInstance := &Protocol{}
	protocolInstance.Init(env)
}

//Init Init
func (p *Protocol) Init(env *protocol.P2PEnv) {
	p.P2PEnv = env
	once.Do(
		func() {
			protocol.RegisterEventHandler(types.EventTxPoolPreRemove, p.handlePreRemove)
			protocol.RegisterEventHandler(types.EventTxPoolPreRemoveByHash, p.handlePreRemoveByHash)
			protocol.RegisterEventHandler(types.EventTxPoolUndoPreRemove, p.handleUndoPreRemoveByHash)
			protocol.RegisterEventHandler(types.EventTxPoolRemoveTxs, p.handleTxPoolRemoveTxs)
			protocol.RegisterEventHandler(types.EventTxPoolPeekTx, p.handleTxPoolPeekTx)
			protocol.RegisterEventHandler(types.EventTxPoolSize, p.handleTxPoolTxNum)
			protocol.RegisterEventHandler(types.EventTxPoolHashes, p.handleTxPoolTxHashes)
		})
}

func (p *Protocol) handlePreRemove(msg *mq.Message) {
	p.Log.Tracef("call pre remove txs")
	num := msg.Data.(int)
	txs, err := p.P2PEnv.TxPool.PreRemoveTransactions(num)
	if err != nil {
		p.Log.Errorf("tx pre remove failed, reason:%v", err)
		msg.Reply(p.WorkClient.NewMessage("", "", types.EventTxPoolPreRemove, err))
		return
	}
	msg.Reply(p.WorkClient.NewMessage("", "", types.EventTxPoolPreRemove, txs))
}

func (p *Protocol) handlePreRemoveByHash(msg *mq.Message) {
	p.Log.Debugf("call pre remove by hash")
	hashes := msg.Data.([]protos.Hash)
	results, err := p.P2PEnv.TxPool.PreRemoveSpecifiedTransactions(hashes)
	if err != nil {
		p.Log.Errorf("tx pre remove failed, reason:%v", err)
		msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, err))
		return
	}
	msg.Reply(p.WorkClient.NewMessage("", "", types.EventTxPoolPreRemoveByHash, results))
}

func (p *Protocol) handleUndoPreRemoveByHash(msg *mq.Message) {
	hashes := msg.Data.([]protos.Hash)
	p.Log.Debugf("call undo pre remove tx, len:%d", len(hashes))
	results, err := p.P2PEnv.TxPool.UndoPreRemoveTransactions(hashes)
	if err != nil {
		p.Log.Errorf("tx pre remove failed, reason:%v", err)
		msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, err))
		return
	}
	msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, results))
}

func (p *Protocol) handleTxPoolRemoveTxs(msg *mq.Message) {
	hashes := msg.Data.([]protos.Hash)
	p.Log.Debugf("call remove txs, size:%d", len(hashes))
	err := p.P2PEnv.TxPool.RemoveTransactions(hashes)
	if err != nil {
		p.Log.Errorf("tx pre remove failed, reason:%v", err)
		msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, err))
		return
	}
	msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, nil))
}

func (p *Protocol) handleTxPoolPeekTx(msg *mq.Message) {
	hash := msg.Data.(protos.Hash)
	tx, err := p.P2PEnv.TxPool.PeekTx(hash)
	if err != nil {
		msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, err))
		return
	}
	msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, tx))
}

func (p *Protocol) handleTxPoolTxNum(msg *mq.Message) {
	msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, p.P2PEnv.TxPool.TxPoolNum()))
}

func (p *Protocol) handleTxPoolTxHashes(msg *mq.Message) {
	num, ok := msg.Data.(uint16)
	if !ok {
		msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, p2perror.ErrWrongType))
		return
	}
	msg.Reply(p.WorkClient.NewMessage("", "", msg.Ty, p.P2PEnv.TxPool.TxPoolHashes(uint(num))))
}
